package com.google.code.mochaccino.framework.dbtool;
/*
 * Copyright 2012 Claude Houle claude.houle@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import ch.qos.logback.core.joran.spi.JoranException;
import com.google.code.mochaccino.framework.bootstrap.Bootstrap;
import com.google.code.mochaccino.framework.dbtool.module.DBToolModule;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.name.Names;
import javax.sql.DataSource;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.hibernate.cfg.Configuration;

public abstract class CommandLine extends Bootstrap {

	public static final String        DIR              = "liquibase";
	public static final String        FILE_NAME_PREFIX = "liquibase-changelog";
	public static final String        FILE_NAME_SUFFIX = ".xml";
	private             Options       options          = null;
	protected           Configuration configuration    = null;
	protected           DataSource    reference        = null;
	protected           DataSource    target           = null;
	protected           Module        module           = null;
	protected           Option        OPT_MODULE       = null;

	/** Constructor */
	public CommandLine() throws JoranException {
		super( false );
	}

	@Override
	protected void interpretArgsAfterInjection( String[] args, Injector injector ) {

		target = injector.getInstance( Key.get( DataSource.class, Names.named( "target" ) ) );
		if ( target == null ) {
			exit( 1, "Supplied module do not have a target datasource" );
		}

		reference = injector.getInstance( Key.get( DataSource.class, Names.named( "reference" ) ) );
		if ( reference == null ) {
			exit( 1, "Supplied module do not have a reference datasource" );
		}

		configuration = injector.getInstance( Key.get( Configuration.class, Names.named( "configuration" ) ) );
		if ( configuration == null ) {
			exit( 1, "Supplied module do not have a hibernate configuration" );
		}

		execute();
	}

	protected abstract void execute();

	@Override
	protected void doStop( Injector injector ) {
	}

	@Override
	protected Module[] getModules() {
		return new Module[] { module };
	}

	protected Options getOptions() {
		if ( options == null ) {
			OPT_MODULE = new Option( "module", true, "The Fully Qualified class name for the modules of the datasources. This module must be instantiable by an empty constructor and extends " + DBToolModule.class.getSimpleName() );
			OPT_MODULE.setArgName( "CLASS" );
			OPT_MODULE.setRequired( true );
			OPT_MODULE.setArgs( 1 );
			options = createOptions();
		}
		return options;
	}

	protected abstract Options createOptions();

	protected void help() {
		HelpFormatter formatter = new HelpFormatter();
		formatter.printHelp( getClass().getName(), getOptions() );
	}

	protected org.apache.commons.cli.CommandLine parseArgs( String[] args ) throws ParseException {
		GnuParser parser = new GnuParser();
		return parser.parse( getOptions(), args );
	}

	@Override
	protected void interpretArgsBeforeInjection( String[] args ) {
		try {
			org.apache.commons.cli.CommandLine parsedArgs = parseArgs( args );
			Class<?> moduleClass = Class.forName( parsedArgs.getOptionValue( OPT_MODULE.getOpt() ) );
			if ( !DBToolModule.class.isAssignableFrom( moduleClass ) ) {
				exit( 1, "Module '" + moduleClass.getName() + "' is not a " + DBToolModule.class.getName() );
			}

			module = (Module) moduleClass.newInstance();


		} catch ( ParseException e ) {
			exit( 1, e );
		} catch ( ClassNotFoundException e ) {
			exit( 1, e );
		} catch ( InstantiationException e ) {
			exit( 1, e );
		} catch ( IllegalAccessException e ) {
			exit( 1, e );
		}
	}
}
